#include "utils/crypto.hpp"

#include <openssl/sha.h>
#include <openssl/rand.h>
#include <sstream>
#include <iomanip>
#include <random>

std::string Crypto::generateSalt() {
    static const char alphanum[] = 
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
    std::string salt;
    salt.reserve(16);   // 设定盐值长16；

    std::random_device rd;
    std::mt19937 gen(rd());

    std::uniform_int_distribution<> distrib(0, sizeof(alphanum) - 2);

    for (size_t i = 0; i < 16; ++i) {
        salt += alphanum[distrib(gen)];
    }

    return salt;
}


std::string Crypto::hashPassword(const std::string& password, const std::string& salt) {
    std::string input = salt + password;
    unsigned char hash[SHA256_DIGEST_LENGTH];

    EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
    const EVP_MD* md = EVP_sha256();

    EVP_DigestInit_ex(mdctx, md, NULL);
    EVP_DigestUpdate(mdctx, input.c_str(), input.size());

    unsigned hash_len;
    EVP_DigestFinal_ex(mdctx, hash, &hash_len);
    EVP_MD_CTX_free(mdctx);

    std::stringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
    }
    return ss.str();
}


bool Crypto::verifyPassword(const std::string& password, const std::string& salt, const std::string& hash) {
    return hashPassword(password, salt) == hash;
}

std::string Crypto::generateFileHash(const std::string& content) {
    return generateFileHash(content.c_str(), content.length());

}

std::string Crypto::generateFileHash(const char* data, size_t size) {
    unsigned char hash[SHA256_DIGEST_LENGTH];

    EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
    const EVP_MD* md = EVP_sha256();

    EVP_DigestInit_ex(mdctx, md, NULL);
    EVP_DigestUpdate(mdctx, data, size);

    unsigned int hash_len;
    EVP_DigestFinal_ex(mdctx, hash, &hash_len);
    EVP_MD_CTX_free(mdctx);  

    std::stringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
    }
    return ss.str();
}
